package com.redis.demo.controller;

import com.redis.demo.pojo.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.TimeUnit;

/**
 * @Author zqf
 * @Date 2022/11/2 10:48
 * @Description: springcache得使用
 */
@RestController("/springCache")
@Api(tags = "springCache注解得使用")
public class SpringCacheController {


    /**
     * @Cacheable可以标记在一个方法上，也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的，当标记在一个类上时则表示该类所有的方法都是支持缓存的 对于一个支持缓存的方法，Spring会在其被调用后将其返回值缓存起来，以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果，
     * 而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的，值就是方法的返回结果
     * @Cacheable参数说明 value：缓存的名称，在spring配置文件中定义，必须指定至少一个
     * Key：缓存的 key，可以为空，如果指定要按照 SpEL 表达式编写，如果不指定，则缺省按照方法的所有参数进行组合
     * condition:缓存的条件，可以为空，使用 SpEL 编写，返回 true 或者 false，只有为 true 才进行缓存
     */
    @GetMapping("/cacheable")
    @ApiOperation("@Cacheable的使用（常用于数据的查询操作）")
    //@Cacheable(value={"cache1","cache2"}
    //@Cacheable(value = "cache1",key = "#cacheKey")
    @Cacheable(value = "cache1", key = "#cacheKey", condition = "#cacheKey.length() > 4")
    public String testSetExpiredKey(@RequestParam("cacheKey") @ApiParam("缓存的key") String cacheKey) {
        //当第一次进来是没有缓存的，会将cacheKey缓存起来，第二次的时候同样的cacheKey会走缓存,不一样的cacheKey会走方法体
        System.out.println("缓存中。。。");
        return "缓存设置成功";
    }


    @PostMapping("/cacheable-param")
    @ApiOperation("@Cacheable的使用，当参数是实体时")
    @Cacheable(value = "cache2", key = "#user.name", condition = "#user.age > 18")
    public String testSetExpiredKey1(@RequestBody User user) {
        //当已缓存的key里面存在user.name时，并且age>18时会走缓存
        System.out.println("缓存中。。。");
        return "缓存设置成功";
    }


    /**
     * @CachePut也可以声明一个方法支持缓存功能。 与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果，
     * 而是每次都会执行该方法，并将执行结果以键值对的形式存入指定的缓存中。
     */
    @GetMapping("/cachePut")
    @ApiOperation("@CachePut存入的使用（常用于对于数据库的新增操作）")
    @CachePut(value = "cache2", key = "#cacheKey")
    public String cachePut(@RequestParam("cacheKey") @ApiParam("缓存的key") String cacheKey) {
        //每次走存缓存
        System.out.println("缓存中。。。");
        return "缓存设置成功";
    }


    /**
     * @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。 当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作
     * @CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。 其中value、key和condition的语义与@Cacheable对应的属性类似。
     * 即value表示清除操作是发生在哪些Cache上的（对应Cache的名称）；
     * key表示需要清除的是哪个key，如未指定则会使用默认策略生成的key；condition表示清除操作发生的条件。
     * allEntries是boolean类型，表示是否需要清除缓存中的所有元素。默认为false，表示不需要。当指定了allEntries为true时，Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素，这比一个一个清除元素更有效率。
     * 清除操作默认是在对应方法成功执行之后触发的，即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间，当我们指定该属性值为true时，Spring会在调用该方法之前清除缓存中的指定元素。
     */
    @GetMapping("/cacheEvict")
    @ApiOperation("@CacheEvict清除缓存的使用（常用于数据的删除操作）")
    @CacheEvict(value = "cache2", key = "#cacheKey", allEntries = false, beforeInvocation = false)
    public String acheEvict(@RequestParam("cacheKey") @ApiParam("清除的key") String cacheKey) {
        //每次走存缓存
        System.out.println("清除中。。。");
        return "缓存清除成功";
    }



    /**
     * @Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。
     * 其拥有三个属性：cacheable、put和evict，分别用于指定@Cacheable、@CachePut和@CacheEvict。
     */
    @Caching(cacheable = @Cacheable(value = "cache1",key = "#cacheKey"), evict = {@CacheEvict("cache2"),
            @CacheEvict(value = "cache3", allEntries = true)})
    @GetMapping("/caching")
    @ApiOperation("@caching组合缓存的使用（常用于数据更新操作，删除旧的缓存，缓存新的）")
    public String caching(@RequestParam("cacheKey") @ApiParam("清除的key") String cacheKey) {
        System.out.println("缓存cache1,清除缓存cache2，cache3");
        return "成功";
    }
}
